-- 版本：V1.0.0

MSB_3E_VICE_HEAD = "5000"
MSB_3E_NET_NO = "00"
MSB_3E_PLC_NO = "FF"
MSB_3E_REQIO_NO = "03FF"
MSB_3E_REQ_NO = "00"
MSB_3E_REQ_ACK_TIME = "0010"
MSB_3E_READ_BATCH_WORDS = "04010000"
MSB_3E_READ_BATCH_COILS = "04010001"
MSB_3E_WRITE_BATCH_COILS = "14010001"
MSB_3E_WRITE_BATCH_WORDS = "14010000"
MSB_3E_RESPONSE_LEN = 22


mAddrLen = 6
mNumLen = 4
mResultHeadLen = MSB_3E_RESPONSE_LEN
mMsgHeader = MSB_3E_VICE_HEAD .. MSB_3E_NET_NO .. MSB_3E_PLC_NO .. MSB_3E_REQIO_NO .. MSB_3E_REQ_NO

--[[
描述:	创建TcpClient对象并连接PLC.

输入:	mPlcIPAddr 		PLC的IP地址
		mPort			PLC的通讯端口号

输出:	ret				错误编号
]]--
function Connect(mPlcIPAddr,mPort)
	netWork = NetClient.New(tostring(mPlcIPAddr),tonumber(mPort))
	ret = netWork:Connect()
	
	return ret
end

--[[
描述:	断开连接.

输入:	无

输出:	ret				错误编号
]]--
function DisConnect()
	ret = netWork:Disconnect()
	
	return ret
end

--[[
描述:	更改地址字符串的格式以及寄存器或线圈的数量.

输入:	addrno 			寄存器或线圈地址
		total			寄存器或线圈总数

输出:	startAddr		更改格式后的地址结果
		numCode			变更格式后的总数结果
]]--
function ChangeFormat(addrno,total)
	
	startAddr = addrno
	
	for ii = #tostring(addrno),mAddrLen - 1 do
		startAddr = "0" .. startAddr
	end
	local numCode = ""
	for ii = #Hex(total),mNumLen - 1 do
		numCode = numCode .. "0"
	end
	numCode = numCode .. Hex(total)
	return startAddr,numCode
end

--[[
描述:	获取数值.

输入:	cmdCode 		命令代码
		reg				寄存器或线圈的名称
		startAddr		得到的第一个地址
		numCode			获得的总数

输出:	retstr			PLC反馈的结果
]]--
function GetBatchValues(cmdCode,reg,startAddr,numCode)

	local sLength = "0000"
	sMsg = MSB_3E_REQ_ACK_TIME  .. cmdCode .. reg .. startAddr .. numCode
	sLength = sLength .. Hex(#sMsg)
	sLength = Mid(sLength,#sLength - 4 + 1,4)	
	sMsg = mMsgHeader .. sLength .. sMsg
	
	while netWork:BufferSize() > 0 do
		local recv = netWork:Receive()
		Thread.Sleep(0.005)
	end
	
	local ErrorCode = netWork:Send(sMsg)
	if ErrorCode < 0 then
		return ErrorCode
	end
	
	local TimeNum = 0
	while netWork:BufferSize() <= 0 do
		Thread.Sleep(0.005)
		TimeNum = TimeNum + 1
		if TimeNum > 600 then
		  print("Receive TimeOut")
		  return -30003
		end
	end
	
	local retstr = netWork:Receive()
	if type(retstr) == "string" then
		return 0,retstr
	else
		return -30007
	end
end

--[[
描述:	设置数值.

输入:	cmdCode 		命令代码
		reg				寄存器或线圈的名称
		startAddr		得到的第一个地址
		numCode			设置的总数
		sValue			设置的数值

输出:	retstr			PLC反馈的结果
]]--
function SetBatchValues(cmdCode,reg,startAddr,numCode,sValue)
	
	sLength = "0000"
	sMsg = MSB_3E_REQ_ACK_TIME  .. cmdCode .. reg .. startAddr .. numCode .. sValue
	sLength = sLength .. Hex(#sMsg)
	sLength = Mid(sLength,#sLength - 4 + 1,4)
	sMsg = mMsgHeader .. sLength .. sMsg
	
	while netWork:BufferSize() > 0 do
		local recv = netWork:Receive()
		Thread.Sleep(0.005)
	end
	
	local ErrorCode = netWork:Send(sMsg)
	if ErrorCode < 0 then
		return ErrorCode
	end
	
	local TimeNum = 0
	while netWork:BufferSize() <= 0 do
		Thread.Sleep(0.005)
		TimeNum = TimeNum + 1
		if TimeNum > 600 then
		  print("Receive TimeOut")
		  return -30003
		end
	end
	
	local retstr = netWork:Receive()
	if type(retstr) == "string" then 
		return 0,retstr
	else
		return -30007
	end
end

--[[
描述:	解析PLC的反馈结果.

输入:	rwFalg 			标志位,读取(true),写入(false)
		wordType		标志位 ,字(true),位(false)
		total			寄存器或线圈总数
		retStr			从PLC反馈的结果

输出:	retstr			解析结果
]]--
function Decode(rwFalg,wordType,total,retStr)
	local strResult = {}
	local result = {}
	if #retStr > 22 then
		valueStr = Mid(retStr, mResultHeadLen+1, #retStr - mResultHeadLen)
		if rwFalg == false then			
			print("Write failed!")
			return -30009
		end
	end
	local dataLen
	if wordType == true then
		dataLen = 4
	else
		dataLen = 1
	end
	
	if #retStr ~= MSB_3E_RESPONSE_LEN + total * dataLen then
		return -30008
	end
	
	if rwFalg == true then
		if (#valueStr)/dataLen == total then
			for ii = 1,total do			
				strResult[ii] = Mid(valueStr,dataLen*(ii - 1) + 1,dataLen)
				result[ii] = tonumber(strResult[ii],16)
				
				if wordType == true then
					if result[ii]&tonumber("8000",16) ~= 0 then						
						result[ii] = result[ii] ~ tonumber("FFFF",16)
						result[ii] = result[ii] + 1
						result[ii] = 0 - result[ii]
					end
				end
			end
		end		
	end

	return 0,result
end

--[[
描述:	读取 D 数据寄存器.

输入:	dwFlag 			标志位,双字(true),单字(false)
		reg				寄存器的名称
		startNo			开始地址
		total			读取寄存器的个数

输出:	iValue			结果返回数组,整型
]]--
function ReadBatchRegisters(dwFlag,reg,startNo,total)
	if dwFlag == true then
		total = 2*total
	end
	
	if reg ~= "D" then
		print("[McPlc.ReadBatchRegisters] - Illegal Register Name!")
		return -30001
	end
	local regCode = reg.."*"
	local startAddr = tostring(startNo)
	local cmdCode = MSB_3E_READ_BATCH_WORDS
	
	local startAddr,numCode = ChangeFormat(startNo,total)
	
	local ErrorCode = 0
	ErrorCode,retvalue = GetBatchValues(cmdCode, regCode, startAddr, numCode)
	if ErrorCode < 0 then
		return ErrorCode
	end
	
	ErrorCode,iValue = Decode(true,true,total,retvalue)	
	if ErrorCode < 0 then
		return ErrorCode
	end
	
	if dwFlag == true then
		local dValue = {}
		for ii = 1,total/2 do
			dValue[ii] = iValue[(ii-1)*2+1]&tonumber("0000FFFF",16)
			TempStr = string.format("%x",iValue[(ii-1)*2+2] << 16)
			
			TempValue = tonumber(TempStr,16)
			TempValue  = TempValue & tonumber("FFFFFFFF",16)
			if TempValue & 80000000 ~= 0 then
				TempValue = TempValue ~ tonumber("FFFFFFFF",16)
				TempValue = TempValue + 1
				TempValue = 0 - TempValue
        
			else
				TempValue = TempValue & tonumber("FFFF0000",16)
			end
			dValue[ii] = dValue[ii] + TempValue	
			if	#Hex(dValue[ii]) < 8 then
				HexValue = Hex(dValue[ii]) 
				for jj = 1,8 - #Hex(dValue[ii]) do
					HexValue = "0" ..HexValue
				end
				dValue[ii] = tonumber(HexValue,16)
			end
			
		end	
		return 0,dValue
	else
		return 0,iValue
	end
	
	
end

--[[
描述:	读线圈的值.

输入:	reg				线圈的名称
		startNo			开始地址
		total			读取线圈的个数

输出:	iValue			结果返回数组
]]--
function ReadBatchCoils(reg,startNo,total)
	if reg ~= "X" and reg ~= "Y" and reg ~= "M" then
		print("[McPlc.ReadBatchCoils] - Illegal Register Name!")
		return -30001
	end
		
	local regCode = reg .. "*"
	local startAddr = tostring(startNo)
	local cmdCode = MSB_3E_READ_BATCH_COILS
	
	local startAddr,numCode = ChangeFormat(startNo,total)
	
	local ErrorCode,retvalue = GetBatchValues(cmdCode, regCode, startAddr, numCode)
	if ErrorCode < 0 then
		return ErrorCode
	end
	
	
	ErrorCode,iValue = Decode(true,false,total,retvalue)
	if ErrorCode < 0 then
		return ErrorCode
	end
	
	for ii=1,#iValue do
		if iValue[ii] == 1 then
			iValue[ii] = true
		else
			iValue[ii] = false
		end
	end
  
	return 0,iValue
end

--[[
描述:	写入寄存器的值.

输入:	dwFlag			标志位,双字(true),单字(false)
		reg				寄存器的名称
		startNo			开始地址
		total			写入寄存器的个数
		iValue			需要写入的数值，数组形式

输出:	无
]]--
function WriteBatchRegisters(dwFlag,reg,startNo,total,iValue)
	local dValue = {}
	
	if reg ~= "D" then
		print("[McPlc.ReadBatchRegisters] - Illegal Register Name!")
		return -30001
	end
	
	if total ~= #iValue then
      print("[McPlc.WriteBatchRegisters] - Register number isn't match to array number!")
      return -30005
    end
	
	if dwFlag == true then
		total = 2*total
	end
	
	local regCode = reg .. "*"
	local startAddr = tostring(startNo)
	local cmdCode = MSB_3E_WRITE_BATCH_WORDS
	
	local startAddr,numCode = ChangeFormat(startNo,total)
	sValue = ""
	if dwFlag == true then
		for ii=1,total/2 do
			if type(iValue[ii]) ~= "number" then
				print("[McPlc.WriteBatchRegisters] - Illegal data!")
				return -30006
			end
			temValue = iValue[ii]
			temStr = Hex(temValue)
			temStr = Trim(temStr," ")
			temStr = "00000000" .. temStr
			temStr = Mid(temStr,#temStr - 8 + 1,8)
			dValue[2*(ii-1)] = Mid(temStr,5,4)
			dValue[2*(ii-1)+1] = Mid(temStr,1,4)
			temStr = dValue[2*(ii-1)] .. dValue[2*(ii-1)+1]
			sValue = sValue .. temStr
		end
	else
		for ii=1,total do
			if type(iValue[ii]) ~= "number" then
				print("[McPlc.WriteBatchRegisters] - Illegal data!")
				return -30006
			end
			temValue = iValue[ii]
			if temValue > 0 then
				temStr = Hex(temValue)
			else
				temValue = math.abs(temValue)
				temValue = temValue ~ tonumber("FFFF",16)
				temValue = temValue + 1
				temStr = Hex(temValue)
			end
			temStr = Trim(temStr," ")
			temStr = "0000" .. temStr
			temStr = Mid(temStr,#temStr - 4 + 1,4)
			sValue = sValue .. temStr
		end
	end
	
	local ErrorCode,retstr = SetBatchValues(cmdCode, regCode, startAddr, numCode, sValue)
	if ErrorCode < 0 then
		return ErrorCode
	end
	
	ErrorCode = Decode(false,false,0,retstr,iValue)	
	if ErrorCode < 0 then
		return ErrorCode
	end
	
	return 0
end

--[[
描述:	写入线圈的值.

输入:	reg				线圈的名称
		startNo			开始地址
		total			写入线圈的个数
		iValue			需要写入的数值，数组形式

输出:	无
]]--
function WriteBatchCoils(reg,startNo,total,bValue)
	if reg ~= "X" and reg ~= "Y" and reg ~= "M" then
		print("[McPlc.ReadBatchCoils] - Illegal Register Name!")
		return -30001
	end
	
	-- 判断线圈个数与数组长度是否匹配
	if total ~= #bValue then
		print("[McPlc.WriteBatchCoils] - Register number isn't match to array number!")
		return -30005
	end
  
	local regCode = reg .. "*"
	local startAddr = tostring(startNo)
	local cmdCode = MSB_3E_WRITE_BATCH_COILS
	
	local startAddr,numCode = ChangeFormat(startNo,total)
	sValue = ""
	for ii=1,total do
		if bValue[ii] == true then
			sValue = sValue .. "1"
		elseif bValue[ii] == false then
			sValue = sValue .. "0"
		else
			print("[McPlc.WriteBatchCoils] - Illegal data!")
			return -30006
		end
	end
	
	local ErrorCode,retstr = SetBatchValues(cmdCode, regCode, startAddr, numCode, sValue)
	if ErrorCode < 0 then
		return ErrorCode
	end
	
	ErrorCode = Decode(false,false,0,retstr,iValue)
	if ErrorCode < 0 then
		return ErrorCode
	end
	
	return 0
end